// Copyright 2008 Philip Allison <sane@not.co.uk>

//    This file is part of Infector.
//
//    Infector is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    Infector is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with Infector.  If not, see <http://www.gnu.org/licenses/>.


//
// Includes
//

// Standard
#ifdef HAVE_CONFIG_H
#	include "config.h"
#endif

// Language headers
#include <memory>
#include <utility>
#include <vector>
#include <list>
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <limits>

// Library headers
#include <glibmm.h>
#include <sigc++/sigc++.h>

// Project headers
#include "gametype.hxx"
#include "boardstate.hxx"
#include "ai.hxx"
#include "ai4.hxx"
#include "game.hxx"


//
// Implementation
//

AI4::AI4(Game *game, const BoardState *bs, const GameType *gt)
	: AI::AI(game, bs, gt)
{
	game->move_made.connect(sigc::mem_fun(this, &AI4::onMoveMade));
	
	// Seed the random number generator (moves are picked at random if there
	// are multiple possibilities with the same score, such as in the opening)
	srand(time(NULL));
	
	// Make a move if it's our turn first
	AI4::onMoveMade(0, 0, 0, 0, false);
}

bool AI4movecmp(const std::pair<move, int> &a, const std::pair<move, int> &b)
{
	return a.second > b.second;
}

void AI4::onMoveMade(const int start_x, const int start_y, const int end_x, const int end_y, const bool gameover)
{
	if (gameover)
		return;

  piece me = m_pBoardState->getPlayer();
  if (m_pGameType->isPlayerType(me, pt_ai4))
  {
    moveTree *movesTree = new moveTree();
    movesTree->trees = NULL;
        
    std::cout << "hello" << std::endl;
    
		std::cout<< "a/b" << alphaBeta(m_pBoardState, 1, 3, -50000, 50000, movesTree) << std::endl;
    //std::cout << alphaBeta(m_pBoardState, 1, 4, -50000, 50000, movesTree) << std::endl;
    //std::cout << alphaBeta(m_pBoardState, 3, -50000, 50000, true, &nextMove, &pruneCounter ) << std::endl;
		//std::cout << alphaBeta(m_pBoardState, 4, -50000, 50000, true, &nextMove, &pruneCounter ) << std::endl;
		
    moveTree *bestTree = movesTree->trees->front();
		m = *(bestTree->the_move);

    std::cout << m.source_x << std::endl;
		std::cout << m.source_y << std::endl;
		std::cout << m.dest_x << std::endl;
		std::cout << m.dest_y << std::endl;
		
		// Highlight the square we're going to move then
		// make the actual move in 0.5 second time increments
		// (to let people see)
		selectpiece = true;
		Glib::signal_timeout().connect(sigc::mem_fun(this, &AI4::makeMove), 500);
	}
}

int AI4::alphaBeta(const BoardState *node, int currDepth, int maxDepth, int alpha, int beta, moveTree *tree)
{
  std::list<move*> preMoves = node->getPossibleMovesListStar(node->getPlayer(), true);
  //std::cout << "currDepth: " << currDepth << " maxDepth: " << maxDepth << std::endl;
  if (currDepth >= maxDepth || preMoves.size() == 0)
  {
    //std::cout << "backing off " << std::endl;
    return heuristic(node);
  }

  if (tree->trees == NULL) {
    tree->trees = new std::list<moveTree*>();
    preMoves = node->getPossibleMovesListStar(node->getPlayer(), false);    
    
    for (std::list<move*>::iterator i = preMoves.begin(); i != preMoves.end(); ++i)
    {
      move *curMove = *i;
      moveTree *newMoveTree = new moveTree();
      newMoveTree->the_move = curMove;
      newMoveTree->trees = NULL;
      tree->trees->push_back(newMoveTree);      
    }    
  }

  std::list<std::pair<int, moveTree*> > pairList;
  BoardState track_b(node->getGameType());  

  for (std::list<moveTree*>::iterator i = tree->trees->begin(); i != tree->trees->end(); ++i)
  {
    moveTree *nextTree = *i;
    move *cur = nextTree->the_move;
    //std::cout << cur->source_x << std::endl;
		//std::cout << cur->source_y << std::endl;
		//std::cout << cur->dest_x << std::endl;
		//std::cout << cur->dest_y << std::endl;
    piece me = node->getPlayer();
    // Simulate the current move
    BoardState new_b(*node);
    int adj = new_b.getAdjacency(cur->source_x, cur->source_y, cur->dest_x, cur->dest_y);
    if (adj == 2) {
	    new_b.setPieceAt(cur->source_x, cur->source_y, pc_player_none);
	  } else if (track_b.getPieceAt(cur->dest_x, cur->dest_y) == pc_no_such_square) {

			continue;
		}
		track_b.setPieceAt(cur->dest_x,cur->dest_y, pc_no_such_square);

    new_b.setPieceAt(cur->dest_x, cur->dest_y, me);
    new_b.nextPlayer();

    int negAlpha = -alphaBeta(&new_b, currDepth+1, maxDepth, -beta, -alpha, nextTree);
    alpha = std::max(alpha, negAlpha);
     
    //std::cout << "adding to pairList " << std::endl;
    
    int size = pairList.size();
    
    for (std::list<std::pair<int, moveTree*> >::iterator j = pairList.begin(); j != pairList.end(); ++j)
    {
      if (negAlpha > j->first)
      {
        std::pair<int, moveTree*> newPair = std::make_pair(negAlpha, nextTree);
        pairList.insert(j, newPair);
        break;
      }
    }
    
    if (pairList.size() == size) {
        std::pair<int, moveTree*> newPair = std::make_pair(negAlpha, nextTree);
        pairList.push_front(newPair);
    }

    if (beta <= alpha)
    {
      // stopping soon, by by!
      std::list<moveTree*> *newList = new std::list<moveTree*>(); 
      for (std::list<std::pair<int, moveTree*> >::iterator j = pairList.begin(); j != pairList.end(); ++j)
      {
        //std::cout << "pushing onto newlist" << std::endl;
        newList->push_back((*j).second);
      } 
      newList->splice(newList->end(), *(tree->trees), i, tree->trees->end() );
      tree->trees = newList;
      return alpha;
    }
  }

     
    
  std::list<moveTree*> *newList = new std::list<moveTree*>(); 
  for (std::list<std::pair<int, moveTree*> >::iterator j = pairList.begin(); j != pairList.end(); ++j)
  {
    //std::cout << "pushing onto newlist" << std::endl;
    newList->push_back((*j).second);
  } 
  tree->trees = newList;
  return alpha;
}

int AI4::heuristic(const BoardState *node)
{
  piece me = node->getPlayer();
  int p1, p2, p3, p4;
  node->getScores(p1, p2, p3, p4);
  switch (me)
  {
    case pc_player_1:
      return p1 - p2;
    case pc_player_2:
      return p2 - p1;
  }
}
